package nl.haemers.collections 
{
	import adobe.utils.CustomActions;
	import nl.haemers.collections.interfaces.IListIterator;
	import nl.haemers.collections.interfaces.ICollection;
	import nl.haemers.collections.interfaces.IIterator;
	import nl.haemers.collections.interfaces.IList;
	
	import nl.haemers.utils.Logger;
	import nl.haemers.collections.errors.UnsupportedOperationException;
	
	/**
	 * @author Jonathan Haemers
	 */
	public class AbstractList extends AbstractCollection implements IList
	{
		protected var modCount				:int = 0;
		
		public function AbstractList() 
		{
			super();
			
		}
		
		/** Public methods **/
		/**
		 * Adds object at the end of the List.
		 * 
		 * @param	object
		 * @return
		 */
		override public function add(e:Object) : Boolean
		{
			addAt(size, e);
			return true;
		}
		
		/**
		 * @inheritDoc
		 */
		public function addAt(location:int, e:Object) : void
		{
			throw new UnsupportedOperationException();
		}
		
		/**
		 * @inheritDoc
		 */
		public function addAllAt(location:int, collection:ICollection) : Boolean
		{
			var it:IIterator = collection.iterator();
			while (it.hasNext()) 
			{
				addAt(location++, it.next());
			}
			
			return !collection.isEmpty();
		}
		
		/**
		 * @inheritDoc
		 */
		override public function clear() : void
		{
			removeRange(0, size);
		}
		
		/**
		 * @inheritDoc
		 */
		override public function equals(e:Object) : Boolean 
		{
			if (this == e) 
			{
				return true;
			}
			
			if (e is IList) 
			{
				var list:IList = e as IList;
				if (list.size != size) 
				{
					return false;
				}

				var it1:IIterator = iterator();
				var it2:IIterator = list.iterator();
				while (it1.hasNext()) 
				{
					var e1:Object = it1.next();
					var e2:Object = it2.next();
					
					if (!(e1 == null ? e2 == null : e1 == e2)) 
					{
						return false;
					}
				}
				
				return true;
			}
			return false;
		}
		
		/**
		 * @inheritDoc
		 */
		override public function hashCode() : int 
		{
			var result:int = 1;
			var it:IIterator = iterator();
			
			while (it.hasNext()) 
			{
				var object:Object = it.next();
				result = (31 * result) + (object == null ? 0 : object.hashCode());
			}
			return result;
		}
		
		/**
		 * @inheritDoc
		 */
		public function indexOf(e:Object) : int
		{
			var it:IListIterator = listIterator();
			if (e != null) {
				while (it.hasNext()) 
				{
					if (e == it.next())
					{
						return it.previousIndex();
					}
				}
			} 
			else 
			{
				while (it.hasNext()) 
				{
					if (it.next() == null) {
						return it.previousIndex();
					}
				}
			}
			return -1;
		}
		
		/**
		 * @inheritDoc
		 */
		override public function iterator() : IIterator
		{
			//We need a reference to this list to call some methods.
			return new Iterator(modCount, this);
		}
		
		/**
		 * @inheritDoc
		 */
		public function lastIndexOf(e:Object) : int
		{
			var it:IListIterator = listIteratorAt(size);
			if (e != null) 
			{
				while (it.hasPrevious()) 
				{
					if (e == it.previous())
					{
						return it.nextIndex();
					}
				}
			} 
			else 
			{
				while (it.hasPrevious()) 
				{
					if (it.previous() == null) 
					{
						return it.nextIndex();
					}
				}
			}
			return -1;
		}
		
		/**
		 * @inheritDoc
		 */
		public function listIterator() : ListIterator
		{
			Logger.i("Listiterator");
			return listIteratorAt(0);
		}
		
		/**
		 * @inheritDoc
		 */
		public function listIteratorAt(location:int) : ListIterator
		{
			return new ListIterator(modCount, this, location);
		}
		
		/**
		 * @inheritDoc
		 */
		public function removeAt(location:int) : Object
		{
			throw new UnsupportedOperationException();
		}
		
		/**
		 * @inheritDoc
		 */
		public function set(location:int, e:Object) : Object
		{
			throw new UnsupportedOperationException();
			return e;
		}
		
		/** Protected methods **/
		protected function removeRange(start:int, end:int) : void
		{
			var it:IIterator = listIteratorAt(start);
			for (var i:int = start; i < end; i++) 
			{
				it.next();
				it.remove();
			}
		}
		
		/**
		 * @inheritDoc
		 */
		public function subList(start:int, end:int) : IList
		{
			return null;
			// TODO: Is this necessary? To create a abstractSubList? Let's keep it abstract for now
			/*
			if (0 <= start && end <= size()) 
			{
				if (start <= end) {
					if (this is RandomAccess) {
						return new SubAbstractListRandomAccess(this, start, end);
					}
					return new SubAbstractList(this, start, end);
				}
				throw new IllegalArgumentException();
			}
				throw new IndexOutOfBoundsException();
			*/
		}
		
		/** Abstract public methods **/
		/**
		 * @inheritDoc
		 */
		public function get(location:int) : Object
		{
			return location;
		}
		
	}
	
	/*
	internal class FullListIterator extends SimpleListIterator implements IListIterator
	{
        public FullListIterator(int start) 
		{
            super();
            if (0 <= start && start <= size()) 
			{
                pos = start - 1;
            }
			else 
			{
                throw new IndexOutOfBoundsException();
            }
        }

        public void add(E object) {
            if (expectedModCount == modCount) {
                try {
                    AbstractList.this.add(pos + 1, object);
                } catch (IndexOutOfBoundsException e) {
                    throw new NoSuchElementException();
                }
                pos++;
                lastPosition = -1;
                if (modCount != expectedModCount) {
                    expectedModCount++;
                }
            } else {
                throw new ConcurrentModificationException();
            }
        }

        public boolean hasPrevious() {
            return pos >= 0;
        }

        public int nextIndex() {
            return pos + 1;
        }

        public E previous() {
            if (expectedModCount == modCount) {
                try {
                    E result = get(pos);
                    lastPosition = pos;
                    pos--;
                    return result;
                } catch (IndexOutOfBoundsException e) {
                    throw new NoSuchElementException();
                }
            }
            throw new ConcurrentModificationException();
        }

        public int previousIndex() {
            return pos;
        }

        public void set(E object) {
            if (expectedModCount == modCount) {
                try {
                    AbstractList.this.set(lastPosition, object);
                } catch (IndexOutOfBoundsException e) {
                    throw new IllegalStateException();
                }
            } else {
                throw new ConcurrentModificationException();
            }
        }
    }

    private static final class SubAbstractListRandomAccess<E> extends
            SubAbstractList<E> implements RandomAccess {
        SubAbstractListRandomAccess(AbstractList<E> list, int start, int end) {
            super(list, start, end);
        }
    }

    private static class SubAbstractList<E> extends AbstractList<E> {
        private final AbstractList<E> fullList;

        private int offset;

        private int size;

        private static final class SubAbstractListIterator<E> implements
                ListIterator<E> {
            private final SubAbstractList<E> subList;

            private final ListIterator<E> iterator;

            private int start;

            private int end;

            SubAbstractListIterator(ListIterator<E> it,
                    SubAbstractList<E> list, int offset, int length) {
                super();
                iterator = it;
                subList = list;
                start = offset;
                end = start + length;
            }

            public void add(E object) {
                iterator.add(object);
                subList.sizeChanged(true);
                end++;
            }

            public boolean hasNext() {
                return iterator.nextIndex() < end;
            }

            public boolean hasPrevious() {
                return iterator.previousIndex() >= start;
            }

            public E next() {
                if (iterator.nextIndex() < end) {
                    return iterator.next();
                }
                throw new NoSuchElementException();
            }

            public int nextIndex() {
                return iterator.nextIndex() - start;
            }

            public E previous() {
                if (iterator.previousIndex() >= start) {
                    return iterator.previous();
                }
                throw new NoSuchElementException();
            }

            public int previousIndex() {
                int previous = iterator.previousIndex();
                if (previous >= start) {
                    return previous - start;
                }
                return -1;
            }

            public void remove() {
                iterator.remove();
                subList.sizeChanged(false);
                end--;
            }

            public void set(E object) {
                iterator.set(object);
            }
        }

        SubAbstractList(AbstractList<E> list, int start, int end) {
            super();
            fullList = list;
            modCount = fullList.modCount;
            offset = start;
            size = end - start;
        }

        @Override
        public void add(int location, E object) {
            if (modCount == fullList.modCount) {
                if (0 <= location && location <= size) {
                    fullList.add(location + offset, object);
                    size++;
                    modCount = fullList.modCount;
                } else {
                    throw new IndexOutOfBoundsException();
                }
            } else {
                throw new ConcurrentModificationException();
            }
        }

        @Override
        public boolean addAll(int location, Collection<? extends E> collection) {
            if (modCount == fullList.modCount) {
                if (0 <= location && location <= size) {
                    boolean result = fullList.addAll(location + offset,
                            collection);
                    if (result) {
                        size += collection.size();
                        modCount = fullList.modCount;
                    }
                    return result;
                }
                throw new IndexOutOfBoundsException();
            }
            throw new ConcurrentModificationException();
        }

        @Override
        public boolean addAll(Collection<? extends E> collection) {
            if (modCount == fullList.modCount) {
                boolean result = fullList.addAll(offset + size, collection);
                if (result) {
                    size += collection.size();
                    modCount = fullList.modCount;
                }
                return result;
            }
            throw new ConcurrentModificationException();
        }

        @Override
        public E get(int location) {
            if (modCount == fullList.modCount) {
                if (0 <= location && location < size) {
                    return fullList.get(location + offset);
                }
                throw new IndexOutOfBoundsException();
            }
            throw new ConcurrentModificationException();
        }

        @Override
        public Iterator<E> iterator() {
            return listIterator(0);
        }

        @Override
        public ListIterator<E> listIterator(int location) {
            if (modCount == fullList.modCount) {
                if (0 <= location && location <= size) {
                    return new SubAbstractListIterator<E>(fullList
                            .listIterator(location + offset), this, offset,
                            size);
                }
                throw new IndexOutOfBoundsException();
            }
            throw new ConcurrentModificationException();
        }

        @Override
        public E remove(int location) {
            if (modCount == fullList.modCount) {
                if (0 <= location && location < size) {
                    E result = fullList.remove(location + offset);
                    size--;
                    modCount = fullList.modCount;
                    return result;
                }
                throw new IndexOutOfBoundsException();
            }
            throw new ConcurrentModificationException();
        }

        @Override
        protected void removeRange(int start, int end) {
            if (start != end) {
                if (modCount == fullList.modCount) {
                    fullList.removeRange(start + offset, end + offset);
                    size -= end - start;
                    modCount = fullList.modCount;
                } else {
                    throw new ConcurrentModificationException();
                }
            }
        }

        @Override
        public E set(int location, E object) {
            if (modCount == fullList.modCount) {
                if (0 <= location && location < size) {
                    return fullList.set(location + offset, object);
                }
                throw new IndexOutOfBoundsException();
            }
            throw new ConcurrentModificationException();
        }

        @Override
        public int size() {
            return size;
        }

        void sizeChanged(boolean increment) {
            if (increment) {
                size++;
            } else {
                size--;
            }
            modCount = fullList.modCount;
        }
    }*/
}
